import FieldExpression from "../impl/FieldExpression";
import Expression from "../interface/Expression";
import MetaData from "../MetaData";
import UnchangedExpression from "../impl/UnchangedExpression";
import CheckError from "../../error/CheckError";
import ObjectUtils from "../../utils/ObjectUtils";

abstract class CoreExpression implements Expression {
    public type:"where" | "limit" | "abs" | "prefix" | "native" | "table" = "abs";

    protected metadata: MetaData;

    constructor(metadata: MetaData) {
        this.metadata = metadata;
        this.metadata.add(this as Expression)
    }

    /** 获取数据 */
    public get(...field: string[]): Promise<Array<Record<string, any>>> {
        this.metadata.status = true;
        this.metadata.fields = field.map(e => new FieldExpression(e, "", this.metadata).toLocaSql()).join(",") || "*";
        this.metadata.add(new UnchangedExpression("select " + this.metadata.fields + " from", this.metadata), 0)
        return this.metadata.core.select(this.metadata)
    }

    /** 获取一条数据 */
    public first(...field: string[]) {
        return this.get(...field).then(e => e?.[0]);
    }

    /** 获取指定字段的值 */
    public value(field: string): Promise<any> {
        return this.first(field).then(e => e?.[field]);
    }

    /** 删除操作 */
    public delete(check: boolean | string | Record<string, string | number | bigint | boolean> = true, value?: string | number | bigint | boolean): Promise<number> {
        this.metadata.status = true;

        // 判断
        if (check === true && !this.metadata.expressionList.some(e => e.type === 'where')) {
            throw new CheckError("不允许全表进行删除操作，如必须操作，请调用时设置check=false");
        } else if (['object', 'string'].includes(typeof check)) {
            if (typeof check === 'string') {
                if (value === undefined || value === null) {
                    throw new CheckError("不允许全表进行删除操作,提供的value无效");
                }
                check = {[check]: value} as Record<string, string | number | bigint | boolean>;
            }
            check = ObjectUtils.purify(check as any);
            let arr = Object.keys(check).map(key => new FieldExpression(key, this.metadata).toLocaSql() + "=?");
            if (arr.length <= 0) {
                throw new CheckError("不允许全表进行删除操作，提供的对象为无效对象")
            }
            // 添加where判断条件
            if (this.metadata.expressionList.some(e => e.type === 'where')) {
                this.metadata.add(new UnchangedExpression("and "))
            } else {
                this.metadata.add(new UnchangedExpression("where"))
            }
            this.metadata.parameter.push(...Object.values(check))
            this.metadata.add(new UnchangedExpression(arr.join(" and ")));
        }
        this.metadata.type = MetaData.DELETE;
        this.metadata.add(new UnchangedExpression("delete from"), 0)
        return this.metadata.core.select(this.metadata).then((e: any) => e.affectedRows);
    }

    /** 插入n条数据，返回成功条数，有bug待修复 */
    public insert(data: Array<Record<string, string | number | bigint | boolean>> | Record<string, string | number | bigint | boolean>): Promise<number> {
        this.metadata.status = true;

        // 参数转换为数组
        if (!Array.isArray(data)) {
            data = [data];
        }
        this.metadata.type = MetaData.INSERT;
        this.metadata.add(new UnchangedExpression("(" + Object.keys(data[0]).map(field => this.metadata.handlSqlField(field)).join(",") + ") values"));
        this.metadata.add(new UnchangedExpression(data.map((info) => "(" + Object.values(info).map(() => `?`).join(",") + ")").join(",")));
        data.forEach(info => {
            this.metadata.parameter.push(...Object.values(info));
        })
        this.metadata.add(new UnchangedExpression("insert into", this.metadata), 0);
        return this.metadata.core.select(this.metadata).then((e: any) => e.affectedRows);
    }

    /** 插入一条数据，返回主键id */
    public insertId(data: Record<string, string | number | bigint | boolean>): Promise<number> {
        this.metadata.status = true;
        this.metadata.type = MetaData.INSERT;
        this.metadata.add(new UnchangedExpression("(" + Object.keys(data).map(field => this.metadata.handlSqlField(field)).join(",") + ") values"));
        this.metadata.add(new UnchangedExpression("(" + Object.values(data).map(() => `?`).join(",") + ")"));
        this.metadata.parameter.push(...Object.values(data))
        this.metadata.add(new UnchangedExpression("insert into", this.metadata), 0);
        return this.metadata.core.select<any>(this.metadata).then(e => e.insertId);
    }

    /** 默认根据主键进行删除，不允许全局修改 */
    public async update(data: Array<Record<string, string | number | bigint | boolean>> | Record<string, string | number | bigint | boolean>): Promise<number> {
        this.metadata.status = true;
        // 参数转换为数组
        if (!Array.isArray(data)) {
            data = [data];
        }
        this.metadata.type = MetaData.UPDATE;
        let par = [...this.metadata.parameter];
        let ex = [...this.metadata.expressionList];
        let index = 0;
        for (let info of data) {
            // 预编译参数
            this.metadata.parameter.splice(0, 0, ...Object.values(info));
            this.metadata.add(new UnchangedExpression("set " + Object.keys(info).map(e => e + "=?").join(",")), 1);
            // 执行操作
            this.metadata.add(new UnchangedExpression("UPDATE", this.metadata), 0)
            await this.metadata.core.select(this.metadata).catch((err) => {
                console.log(err)
                index--;
            });
            index++;
            this.metadata.parameter = [...par];
            this.metadata.expressionList = [...ex]
        }
        return index;
    }

    public toSql(completion: boolean = false): string {
        let sql = this.metadata.expressionList.map(e => e.toLocaSql()).join(" ");
        if (completion) {
            let i = 0;
            sql = sql.replace(/\?/g, () => {
                return this.metadata.parameter[i++];
            })
        }
        return sql;
    };

    public setMetadata(metadata: MetaData) {
        this.metadata = metadata;
    }

    abstract toLocaSql(): string;
}

export = CoreExpression;